/*
 *	pg_upgrade_support.c
 *
 *	服务器端函数，用于设置后端全局变量
 *	以控制 oid 和 relfilenode 分配，以及进行 pg_upgrade 所需的其他特殊
 *	操作。
 *
 *	Copyright (c) 2010-2022, PostgreSQL Global Development Group
 *	src/backend/utils/adt/pg_upgrade_support.c
 */

#include "postgres.h"

#include "catalog/binary_upgrade.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/extension.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"


#define CHECK_IS_BINARY_UPGRADE									\
do {															\
	if (!IsBinaryUpgrade)										\
		ereport(ERROR,											\
				(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),	\
				 errmsg("function can only be called when server is in binary upgrade mode"))); \
} while (0)

Datum binary_upgrade_set_next_pg_tablespace_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_tbspoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_pg_tablespace_oid = fc_tbspoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_pg_type_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_typoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_pg_type_oid = fc_typoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_typoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_array_pg_type_oid = fc_typoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_typoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_mrng_pg_type_oid = fc_typoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_typoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_mrng_array_pg_type_oid = fc_typoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_heap_pg_class_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_reloid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_heap_pg_class_oid = fc_reloid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_heap_relfilenode(PG_FUNCTION_ARGS)
{
	Oid			fc_nodeoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_heap_pg_class_relfilenode = fc_nodeoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_index_pg_class_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_reloid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_index_pg_class_oid = fc_reloid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_index_relfilenode(PG_FUNCTION_ARGS)
{
	Oid			fc_nodeoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_index_pg_class_relfilenode = fc_nodeoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_toast_pg_class_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_reloid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_toast_pg_class_oid = fc_reloid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_toast_relfilenode(PG_FUNCTION_ARGS)
{
	Oid			fc_nodeoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_toast_pg_class_relfilenode = fc_nodeoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_pg_enum_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_enumoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_pg_enum_oid = fc_enumoid;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_next_pg_authid_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_authoid = PG_GETARG_OID(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_next_pg_authid_oid = fc_authoid;
	PG_RETURN_VOID();
}

Datum binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS)
{
	text	   *fc_extName;
	text	   *fc_schemaName;
	bool		fc_relocatable;
	text	   *fc_extVersion;
	Datum		fc_extConfig;
	Datum		fc_extCondition;
	List	   *fc_requiredExtensions;

	CHECK_IS_BINARY_UPGRADE;

	/* 在解除引用参数之前必须检查这些内容 */
	if (PG_ARGISNULL(0) ||
		PG_ARGISNULL(1) ||
		PG_ARGISNULL(2) ||
		PG_ARGISNULL(3))
		elog(ERROR, "null argument to binary_upgrade_create_empty_extension is not allowed");

	fc_extName = PG_GETARG_TEXT_PP(0);
	fc_schemaName = PG_GETARG_TEXT_PP(1);
	fc_relocatable = PG_GETARG_BOOL(2);
	fc_extVersion = PG_GETARG_TEXT_PP(3);

	if (PG_ARGISNULL(4))
		fc_extConfig = PointerGetDatum(NULL);
	else
		fc_extConfig = PG_GETARG_DATUM(4);

	if (PG_ARGISNULL(5))
		fc_extCondition = PointerGetDatum(NULL);
	else
		fc_extCondition = PG_GETARG_DATUM(5);

	fc_requiredExtensions = NIL;
	if (!PG_ARGISNULL(6))
	{
		ArrayType  *fc_textArray = PG_GETARG_ARRAYTYPE_P(6);
		Datum	   *fc_textDatums;
		int			fc_ndatums;
		int			fc_i;

		deconstruct_array(fc_textArray,
						  TEXTOID, -1, false, TYPALIGN_INT,
						  &fc_textDatums, NULL, &fc_ndatums);
		for (fc_i = 0; fc_i < fc_ndatums; fc_i++)
		{
			char	   *fc_extName = TextDatumGetCString(fc_textDatums[fc_i]);
			Oid			fc_extOid = get_extension_oid(fc_extName, false);

			fc_requiredExtensions = lappend_oid(fc_requiredExtensions, fc_extOid);
		}
	}

	InsertExtensionTuple(text_to_cstring(fc_extName),
						 GetUserId(),
						 get_namespace_oid(text_to_cstring(fc_schemaName), false),
						 fc_relocatable,
						 text_to_cstring(fc_extVersion),
						 fc_extConfig,
						 fc_extCondition,
						 fc_requiredExtensions);

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_record_init_privs(PG_FUNCTION_ARGS)
{
	bool		fc_record_init_privs = PG_GETARG_BOOL(0);

	CHECK_IS_BINARY_UPGRADE;
	binary_upgrade_record_init_privs = fc_record_init_privs;

	PG_RETURN_VOID();
}

Datum binary_upgrade_set_missing_value(PG_FUNCTION_ARGS)
{
	Oid			fc_table_id = PG_GETARG_OID(0);
	text	   *fc_attname = PG_GETARG_TEXT_P(1);
	text	   *fc_value = PG_GETARG_TEXT_P(2);
	char	   *fc_cattname = text_to_cstring(fc_attname);
	char	   *fc_cvalue = text_to_cstring(fc_value);

	CHECK_IS_BINARY_UPGRADE;
	SetAttrMissing(fc_table_id, fc_cattname, fc_cvalue);

	PG_RETURN_VOID();
}
